package multithread.framework.threadmethod;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

import multithread.framework.annotation.ThreadAfter;
import multithread.framework.annotation.ThreadAllAfter;
import multithread.framework.annotation.ThreadAllBefore;
import multithread.framework.annotation.ThreadBefore;
import multithread.framework.annotation.ThreadFor;
import multithread.framework.annotation.ThreadGroupAfter;
import multithread.framework.annotation.ThreadGroupBefore;
import multithread.framework.annotation.ThreadGroupFor;
import multithread.framework.utility.Utilities;

import org.apache.log4j.Logger;
/**
 * package one test method and multithreading info
 * @see ThreadMethodGroup
 * @author dailey_dai
 *
 */
public class ThreadMethod implements IThreadMethod {
	private Method ivokeMthod;
	private ThreadGroupFor threadGroupFor;
	private ThreadFor threadFor;
	private ThreadAllBefore threadAllBefore;
	private ThreadAllAfter threadAllAfter;
	private ThreadGroupBefore threadGroupBefore;
	private ThreadGroupAfter threadGroupAfter;
	private ThreadBefore threadBefor;
	private ThreadAfter threadAfter;
	private Object[] methodParams;
	private Class<?>[] methodParamTypes;

	public ThreadMethod() {
	}

	public ThreadMethod(Method ivokeMthod) {
		setIvokeMthod(ivokeMthod);
	}

	public FutureQueue<ThreadResult> invoke(ExecutorService service,
			final Object instance,
			Class<? extends Annotation>... annotationClzs) {
		FutureQueue<ThreadResult> futureQueue = new FutureQueue<ThreadResult>();
		// invoke those thread [all]before [all]after method
		if (Utilities.containNotNeedRunMultiThreadAnnotation(annotationClzs)) {
			futureQueue.add(_invoke(service, instance));
			return futureQueue;
		}
		// ignore threadFor.value()==0
		if (threadFor == null) {
			// --------------------------------------------------------------------------------------------------------------
			// how to take it?
			// --------------------------------------------------------------------------------------------------------------
		} else {
			for (int i = 0; i < threadFor.value(); i++) {
				Future<ThreadResult> result = _invoke(service, instance);
				futureQueue.add(result);
			}
		}
		return futureQueue;
	}

	public FutureQueue<ThreadResult> invoke(
			CompletionService<ThreadResult> service, final Object instance,
			Class<? extends Annotation>... annotationClzs) {
		FutureQueue<ThreadResult> futureQueue = new FutureQueue<ThreadResult>();
		// invoke those thread [all]before [all]after method
		if (Utilities.containNotNeedRunMultiThreadAnnotation(annotationClzs)) {
			futureQueue.add(_invoke(service, instance));
			return futureQueue;
		}
		// ignore threadFor.value()==0
		if (threadFor == null) {
			// --------------------------------------------------------------------------------------------------------------
			// how to take it?
			// --------------------------------------------------------------------------------------------------------------
		} else {
			for (int i = 0; i < threadFor.value(); i++) {
				Future<ThreadResult> result = _invoke(service, instance);
				futureQueue.add(result);
			}
		}
		return futureQueue;

	}

	/**
	 * 
	 * @param service
	 *            CompletionService
	 * @param instance
	 *            test case
	 * @return
	 */
	private Future<ThreadResult> _invoke(
			final CompletionService<ThreadResult> service, final Object instance) {
		return service.submit(new Callable<ThreadResult>() {
			@Override
			public ThreadResult call() throws Exception {
				try {
					final Object result = ivokeMthod.invoke(instance,
							methodParams);
					if (result instanceof ThreadResult) {
						return (ThreadResult) result;
					}
					return new ThreadResult() {
						@Override
						public Object getResult() {
							return result;
						}
					};
				} catch (Exception e) {
					Logger.getLogger(this.getClass()).fatal(ivokeMthod, e);
					throw e;
				}
			}
		});
	}

	/**
	 * 
	 * @param service
	 *            ExecutorService
	 * @param instance
	 *            test case
	 * @return
	 */
	private Future<ThreadResult> _invoke(ExecutorService service,
			final Object instance) {
		return service.submit(new Callable<ThreadResult>() {
			@Override
			public ThreadResult call() throws Exception {
				try {
					final Object result = ivokeMthod.invoke(instance,
							methodParams);
					if (result instanceof ThreadResult) {
						return (ThreadResult) result;
					}
					return new ThreadResult() {
						@Override
						public Object getResult() {
							return result;
						}
					};
				} catch (Exception e) {
					Logger.getLogger(this.getClass()).fatal(ivokeMthod, e);
					throw e;
				}
			}
		});
	}

	@Override
	public int getThreadCount() {
		return (threadFor == null || threadFor.value() < 0) ? 0 : threadFor
				.value();
	}

	@Override
	public String toString() {
		return "ThreadMethod [ivokeMthod=" + ivokeMthod + "]";
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ThreadMethod other = (ThreadMethod) obj;
		if (ivokeMthod == null) {
			if (other.ivokeMthod != null)
				return false;
		} else if (!ivokeMthod.equals(other.ivokeMthod))
			return false;
		return true;
	}

	public Method getIvokeMthod() {
		return ivokeMthod;
	}

	public void setIvokeMthod(Method ivokeMthod) {
		this.ivokeMthod = ivokeMthod;
	}

	public ThreadGroupFor getThreadGroupFor() {
		return threadGroupFor;
	}

	public void setThreadGroupFor(ThreadGroupFor threadGroupFor) {
		this.threadGroupFor = threadGroupFor;
	}

	public ThreadFor getThreadFor() {
		return threadFor;
	}

	public void setThreadFor(ThreadFor threadFor) {
		this.threadFor = threadFor;
	}

	public ThreadAllBefore getThreadAllBefore() {
		return threadAllBefore;
	}

	public void setThreadAllBefor(ThreadAllBefore threadAllBefor) {
		this.threadAllBefore = threadAllBefor;
	}

	public ThreadAllAfter getThreadAllAfter() {
		return threadAllAfter;
	}

	public void setThreadAllAfter(ThreadAllAfter threadAllAfter) {
		this.threadAllAfter = threadAllAfter;
	}

	public ThreadGroupBefore getThreadGroupBefore() {
		return threadGroupBefore;
	}

	public void setThreadGroupBefore(ThreadGroupBefore threadGroupBefore) {
		this.threadGroupBefore = threadGroupBefore;
	}

	public ThreadGroupAfter getThreadGroupAfter() {
		return threadGroupAfter;
	}

	public void setThreadGroupAfter(ThreadGroupAfter threadGroupAfter) {
		this.threadGroupAfter = threadGroupAfter;
	}

	public ThreadBefore getThreadBefor() {
		return threadBefor;
	}

	public void setThreadBefor(ThreadBefore threadBefor) {
		this.threadBefor = threadBefor;
	}

	public ThreadAfter getThreadAfter() {
		return threadAfter;
	}

	public void setThreadAfter(ThreadAfter threadAfter) {
		this.threadAfter = threadAfter;
	}

	public Object[] getMethodParams() {
		return methodParams;
	}

	public void setMethodParams(Object[] methodParams) {
		this.methodParams = methodParams;
	}

	public Class<?>[] getMethodParamTypes() {
		return methodParamTypes;
	}

	public void setMethodParamTypes(Class<?>[] methodParamTypes) {
		this.methodParamTypes = methodParamTypes;
	}

}
